#ifndef _MOEA_STATISTICS_SINK
#define _MOEA_STATISTICS_SINK

#include "engine/Statistics.hh"
#include "engine/netstream.hh"
#include "engine/MOEA.hh"
#include <iostream>

using namespace std;
/**When the algorithm runs in parallel, this class runs as a mpi process
  *with the porpouse of receive and merge the statistics of the other
  *process (islands)
  */
class StatisticsSink {

private:

    Statistics *statistics;
    NetStream *stream;
    MOEA *moea;
    int populationSize;


public:

    StatisticsSink(Statistics *statistics, NetStream *stream, MOEA *moea, int populationSize) {
        this->statistics = statistics;
        this->stream = stream;
        this->moea = moea;
        this->populationSize = populationSize;

    }

    virtual ~StatisticsSink() {
        delete statistics;
    }

    Statistics *current() {
        return statistics;
    }

    void run() {
        int processes = stream->pnumber();
        int end = false;

        while (!end) {
            (*stream) << barrier;

            statistics->reset();

            for (int i = 1; i < processes; i++) {

                (*stream) << set_source(i);

                (*stream) << wait(packed);
                (*stream) << pack_begin;
                Statistics *newStatistics = statistics->receive();
                (*stream) << pack_end;

                end = newStatistics->ended();

                statistics->update(newStatistics);


                delete newStatistics;
            }

            cout << endl;

            cout << "[partial results]" << endl;

            cout << "---------------------------------------------------" << endl;

            statistics->send(cout);

            cout << "---------------------------------------------------" << endl;

            cout << endl;
        }


        ArrayList<Solution> *solutions = statistics->getParetoFront();
        ArrayList<Solution> *populationSolutions = new ArrayList<Solution>(true);

        for (int i = 0; i < solutions->length(); i++)
            populationSolutions->add(solutions->get(i)->copy());

        int solutionsSize = solutions->length();
        //'stuffing' the new population
        while (populationSolutions->length() < populationSize)
            populationSolutions->add(solutions->get(Random::getInstance()->getInt(solutionsSize))->copy());

        cout << "[running panmitic integration]" << endl;

        Population *population = new Population(populationSolutions);
        integration(population);
        delete population;
    }

private:


    void integration(Population *population) {

        //statistics will also be an observer of the moea
        statistics->reset();

        //disconnect from the islands statistics, (at this point there will be no islands)
        statistics->setNetStream(NULL);

        moea->addObserver(statistics);
        moea->setCurrent(population);
        moea->run();
    }
};

#endif
